﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization;
using System.Threading;
using System.Messaging;

namespace TestClient
{
    public partial class Summary : Form
    {
        static private Inventory g_inventory;
        private Thread g_thread;
        //
        // Define a delegate function prototype and define the function
        //
        private delegate void ListViewCheckInvoker();
        private void ListViewCheck()
        {
            MessageQueue messagequeue = new MessageQueue(@".\Private$\RecordsTestQueue");
            for (int i = 0; i < listViewEmployees.Items.Count; i++)
            {
                ListViewItem item = listViewEmployees.Items[i];
                Employee employee = (Employee)item.Tag;
                if (listViewEmployees.Items[i].Checked != employee.viewable)
                {
                    // The user has uncheck the GUI and the content is still the old one
                    // First of all, let us update the list view itself.
                    employee.viewable = listViewEmployees.Items[i].Checked;
                    listViewEmployees.Items[i].Tag = employee;

                    // This is where the problem, this value is set. However, it is not saved into
                    // XML file so that when another application is started
                    // It started with the initial value and will also send out
                    // message to other clients. Ideally, the information
                    // should be stored first so that it can be consistent
                    // across the board.

                    // It is time to notify other application this change.
                    messagequeue.Send(employee, "Summary of Employees Check");
                }
            }
        }

        private delegate void MethodInvoker(Employee employee);
        private void UpdateListView(Employee employee)
        {
            // ok, we have got the updated information, now it is time 
            // to update those information into the GUI.
            // OK, now we have get the data and we should initial the data on the screen
            // and all the records as checked.

            // We need to have a better algorith to handle the situation
            // This is the algorithm, we are going through each item in the global array
            // and the item in the list view. 

            // The revised version, let us check the listview directly.
            // If there is an existing item, let us update it and set it to be visible.
            // If there is no existing item, let us add it and set it to be visible.
            bool bFind = false;
            bool bUpdate = false;
            int location = -1;

            for(int i=0; i< listViewEmployees.Items.Count; i++)
            {
                // The first item is the box

               // let us check whether it is a new item or updated item.
                if (employee.name.Equals(listViewEmployees.Items[i].Text))
                {
                    // Find the entry, it is an update
                    bFind = true;
                    location = i;
                    break;
                }
            }

            ListViewItem item = new ListViewItem();  
            if (bFind)
            {
                // This is for updating the existing item
                item = listViewEmployees.Items[location];

                // The content is changed.
                if (!employee.employeeid.Equals(item.SubItems[1].Text) ||
                    !employee.position.Equals(item.SubItems[2].Text) ||
                    !employee.date.Equals(item.SubItems[3].Text))
                {
                    // The content is changed. Let us change the content 
                    item.Tag = employee;
                    item.SubItems[1].Text = employee.employeeid;
                    item.SubItems[2].Text = employee.position;
                    item.SubItems[3].Text = employee.date;
                    bUpdate = true;
                }
                else
                {
                    // If no content is changed, then the only possiblity is the check box is changed.
                    if (item.Checked != employee.viewable)
                    {
                        item.Checked = employee.viewable;
                        item.Tag = employee;
                        bUpdate = true;
                    }
                }
            }
            else
            {
                // This is for the new items.
                item.Tag = employee;
                item.Checked = employee.viewable;
                item.Text = employee.name;
                item.SubItems.Add(employee.employeeid);
                item.SubItems.Add(employee.position);
                item.SubItems.Add(employee.date);
                listViewEmployees.Items.Add(item);
                bUpdate = true;
            }

            // OK, it is either a new item or an updated item.
            if (bUpdate)
            {
                // Set the item to be visiable and finsih the finding.
                item.EnsureVisible();
            }          
        }


        //
        // Function: 
        // (1) This function is used to trace the message queue
        //           It calls the delegate function in order to update the ListView
        // (2) This function is used to trace the check status for the listView
        //           a certain item is checked or unchecked, then the check status
        //           should be reported to a queue and then the status is updated accordingly
        private void PeekMessages()
        {
            // Connect to the message queue
            MessageQueue messagequeue = new MessageQueue(@".\Private$\RecordsTestQueue");
            while (messagequeue != null)
            {
                System.Type[] types = new Type[1];
                types[0] = typeof(Employee);
                messagequeue.Formatter = new XmlMessageFormatter(types);
                foreach (System.Messaging.Message message in messagequeue)
                {
                    // The key here is the latest employee object is saved
                    // into the list view itself. Let us update the 
                    // list view directly and also update the employee object
                    Employee employee = message.Body as Employee;
                    Invoke(new MethodInvoker(UpdateListView), employee);
                }
                // Sleep for a certain period
                Thread.Sleep(5000);

                // Do not access GUI directly
                // Check the check boxes status
                Invoke(new ListViewCheckInvoker(ListViewCheck),null);
                Thread.Sleep(5000);

            }
        }

        public Summary()
        {
            InitializeComponent();
        }

        public void getEmployeeRecords()
        {
            FileStream f = null;
            try
            {
                // This function is used to get the employess from the XML file
                // Create the atributes
                XmlAttributes attrs = new XmlAttributes();
                attrs.XmlElements.Add(new XmlElementAttribute("Employee", typeof(Employee)));

                // Ceate the overrids 
                XmlAttributeOverrides attOver = new XmlAttributeOverrides();
                attOver.Add(typeof(Inventory), "InventoryItems", attrs);

                // Output to employees.xml 
                f = new FileStream("c:\\employees.xml", FileMode.OpenOrCreate);
                XmlSerializer sr = new XmlSerializer(typeof(Inventory), attOver);
                g_inventory = (Inventory)sr.Deserialize(f);
                f.Close();

                // OK, now we have get the data and we should initial the data on the screen
                // and all the records as checked.
                int length = g_inventory.InventoryItems.GetLength(0);

                // Clean up all items.
                listViewEmployees.Items.Clear();
                if (length > 0)
                {
                    for (int i = 0; i < length; i++)
                    {
                        Employee employee = g_inventory.InventoryItems[i];
                        ListViewItem item = new ListViewItem();
                        item.Checked = employee.viewable;
                        item.Text = employee.name;
                        item.SubItems.Add(employee.employeeid);
                        item.SubItems.Add(employee.position);
                        item.SubItems.Add(employee.date);
                        item.Tag = employee;
                        // Add the items to the list view
                        listViewEmployees.Items.Add(item);
                    }
                    // OK, now we have added items into the list view
                    // the next thing is to sort the elements
                    // listViewEmployees.Sort();
                }
            }
            catch (Exception e)
            {
                // Console.WriteLine(e);
                // close the file handle
                if (f != null) f.Close();
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // initia the data dispaly
            getEmployeeRecords();

            // Initial a thread to keep trace of messages
            g_thread = new Thread(PeekMessages);
            g_thread.IsBackground = true;
            g_thread.Start();
        }

        private void Summary_FormClosed(object sender, FormClosedEventArgs e)
        {
            // stop the thread.
            if (g_thread != null) g_thread.Abort();
        }

        private void listViewEmployees_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            // Origially, I plan to use this place to send the check/uncheck object
            // to the message queue. However, this API is called many times
            // during the process. This is not a right place to update the message queue.
        }
    }


    public class Employee
    {
        public string name;
        public string employeeid;
        public string position;
        public string date;
        public bool viewable;
    }

    // All the employees are considered part of the company inventory.
    [Serializable]
    public class Inventory
    {
        private Employee[] employees;
        // private List<Employee> employees;
        public Inventory() { }
        [XmlArrayItem("Employee", typeof(Employee))]
        public Employee[] InventoryItems
        {
            get { return employees; }
            set { employees = value; }
        }
    }
}
